package cn.zz.veggie.application.service;

import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.stream.Collectors;

import org.springframework.stereotype.Service;

import cn.zz.comm.core.types.query.PageQuery;
import cn.zz.comm.helper.BizCommHelper;
import cn.zz.comm.types.result.P;
import cn.zz.comm.util.CollectorUtil;
import cn.zz.comm.util.DateTimeUtil;
import cn.zz.veggie.application.assembler.SettleAssembler;
import cn.zz.veggie.application.packer.SettlePacker;
import cn.zz.veggie.model.domain.Order;
import cn.zz.veggie.model.domain.Settle;
import cn.zz.veggie.model.domain.Shop;
import cn.zz.veggie.model.query.OrderQuery;
import cn.zz.veggie.model.query.SettleQuery;
import cn.zz.veggie.persistence.repository.OrderRepository;
import cn.zz.veggie.persistence.repository.SettleRepository;
import cn.zz.veggie.persistence.repository.ShopRepository;
import cn.zz.veggie.web.types.dto.mgmt.settle.MgmtSettleCreateDto;
import cn.zz.veggie.web.types.dto.mgmt.settle.MgmtSettleQueryDto;
import cn.zz.veggie.web.types.vo.mgmt.MgmtSettleVo;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;

/**
 * @author zhongq
 * @since 2024/6/7
 */
@Slf4j
@Service
@RequiredArgsConstructor
public class SettleAppService {
	private final OrderRepository orderRepository;
	private final SettleRepository settleRepository;
	private final ShopRepository shopRepository;

	public P<MgmtSettleVo> mgmtGetList(MgmtSettleQueryDto dto, PageQuery page) {
		SettleQuery query = SettleAssembler.buildAdminQuery(dto);
		List<Settle> settles = settleRepository.findByPage(query, page);
		Set<Integer> shopIds = CollectorUtil.bws(settles, Settle::getShopId);
		Map<Integer, Shop> shopMap = shopRepository.findByIds(shopIds);
		List<MgmtSettleVo> vos = CollectorUtil.bwl(settles,
				s -> SettlePacker.toMgmtSettleVo(s, shopMap.get(s.getShopId())));
		return P.of(page, vos);
	}

	public void timerSettleStatistics() {
		final long timeStart = DateTimeUtil.monthStart(-1);
		final long timeEnd = DateTimeUtil.monthStart(0);
		List<Order> orders = orderRepository.findByPage(OrderQuery.ofDelivery(timeStart, timeEnd), PageQuery.all());
		List<Settle> existSettles = settleRepository.findByPage(SettleQuery.of(timeStart), PageQuery.all());
		final Map<Integer, Settle> shopSettleMap = CollectorUtil.btmByKeyFun(existSettles, Settle::getShopId);
		Map<Integer, List<Order>> shopOrdersMap = orders.stream().collect(Collectors.groupingBy(Order::getShopId));
		// collect
		List<Settle> settles = shopOrdersMap.entrySet().stream().map(entry -> {
			final Integer shopId = entry.getKey();
			final List<Order> userOrders = entry.getValue();
			final Settle settle = Optional.of(shopId).map(shopSettleMap::get)
					.orElseGet(() -> SettleAssembler.createSettle(shopId, timeStart, timeEnd));
			final int totalAmount = userOrders.stream().mapToInt(Order::getAmount).sum();
			SettleAssembler.updateSettle(settle, userOrders.size(), totalAmount);
			return settle;
		}).toList();
		settleRepository.batchSave(settles);
	}

	public void adminRefresh(Integer id) {
		final Settle settle = settleRepository.findById(id);
		BizCommHelper.requireNoNull(settle, "settle");
		syncSettleStat(settle);
	}

	public void mgmtCreateSettle(MgmtSettleCreateDto dto) {
		final Long statTime = dto.getStatTime();
		final SettleQuery settleQuery = SettleQuery.of(statTime);
		Settle settle = settleRepository.findOne(settleQuery);
		if (settle != null) {
			log.warn("settle is exist not force {} , record {}", statTime, settle.getId());
		}
		syncSettleStat(SettleAssembler.createSettle(dto.getShopId(), dto.getStatTime(), dto.getStatTimeEnd()));
	}

	public void adminMarkComplete(Integer id) {
		final Settle settle = settleRepository.findById(id);
		BizCommHelper.requireNoNull(settle, "settle");
		SettleAssembler.markComplete(settle);
		settleRepository.save(settle);
	}

	private void syncSettleStat(Settle settle) {
		OrderQuery query = OrderQuery.ofShopDelivery(settle.getShopId(), settle.getStatTime(), settle.getStatTimeEnd());
		List<Order> orders = orderRepository.findWithPage(query, PageQuery.all());
		settle.setOrderCount(orders.size());
		settle.setAmount(orders.stream().mapToInt(Order::getAmount).sum());
		settleRepository.save(settle);
	}

}